﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "GnufBase.h"

namespace gnuf {

    typedef gcmp::WeakPtr< gcmp::WeakReferenceable > ObjectWatcher;

    template <class TObject>
    class ObjectWeakPtr final
    {
    public:
        ObjectWeakPtr() : m_pObject(nullptr) {}

        ObjectWeakPtr(TObject * pObject) : m_pObject(pObject)
        {
            if (nullptr != m_pObject)
            {
                m_parasite.Reset(m_pObject->parasite());
            }
        }

        ObjectWeakPtr(const ObjectWeakPtr & that) : m_pObject(nullptr)
        {
            if (that.isValid())
            {
                m_pObject  = that.m_pObject;
                m_parasite = that.m_parasite;
            }
        }
        
        ~ObjectWeakPtr()
        {
            m_pObject = nullptr;
            m_parasite.Reset(nullptr);
        }

        ObjectWeakPtr & operator=(const ObjectWeakPtr & that)
        {
            if (that.isValid())
            {
                m_pObject  = that.m_pObject;
                m_parasite = that.m_parasite;
            }
            else
            {
                m_pObject = nullptr;
                m_parasite.Reset(nullptr);
            }
            return *this;
        }

        ObjectWeakPtr & operator=(TObject * pObject)
        {
            if (nullptr != pObject)
            {
                m_pObject = pObject;
                m_parasite.Reset(pObject->parasite());
            }
            else
            {
                m_pObject = nullptr;
                m_parasite.Reset(nullptr);
            }
            return *this;
        }

        bool isValid() const { return m_parasite.IsValid(); }

        operator bool() const { return isValid(); }

        bool operator!() const { return !isValid(); }

        TObject * operator->() { return get(); }

        const TObject * operator->() const { return get(); }

        TObject * get()
        {
            // If invalid, return nullptr even if the m_pObject is not nullptr.
            return (m_parasite.IsValid() ? m_pObject : nullptr);
        }

        const TObject * get() const
        {
            // If invalid, return nullptr even if the m_pObject is not nullptr.
            return (m_parasite.IsValid() ? m_pObject : nullptr);
        }

    private:
        TObject *     m_pObject;
        ObjectWatcher m_parasite;
    };
}

#define GNUF_WATCHABLE_OBJECT(ClassName) \
    public: \
        gcmp::WeakReferenceable * objectOf##ClassName()   { return & m_objectOf##ClassName; } \
        gcmp::WeakReferenceable * parasite()              { return & m_objectOf##ClassName; } \
    private: \
        gcmp::WeakReferenceable m_objectOf##ClassName;

#define GNUF_WATCHING_THIS \
    gnuf::ObjectWatcher wathchingMyself( this );

#define GNUF_WATCHING_SELF \
    gnuf::ObjectWatcher wathchingMyself( parasite() );

#define GNUF_THIS_IS_VALID \
    wathchingMyself.IsValid()

#define GNUF_THIS_IS_NOT_VALID \
    !wathchingMyself.IsValid()

#define GNUF_IF_INVALID_RETURN(Value) \
    if ( !wathchingMyself.IsValid() ) \
        return Value;

#define GNUF_IF_INVALID_RETURN_VOID \
    if ( !wathchingMyself.IsValid() ) \
        return;

#define GNUF_IF_INVALID_RETURN_FALSE \
    if ( !wathchingMyself.IsValid() ) \
        return false;
